#ifndef _UTILITY_HPP_
#define _UTILITY_HPP_
#include "defs.h"
#include <iomanip>
#include <iostream>
#include <iomanip>
#include <string>

struct Transij
{
	// 交换
	static const int transI[4];
	// 不交换
	static const int transJ[4];
	const int (&trans)[4];

	explicit Transij(bool isIfaceOrSwapIJ) : trans{isIfaceOrSwapIJ ? transI : transJ} {}
	Transij(const Transij &src) = delete;
	Transij(Transij &&src) = delete;
	Transij &operator=(const Transij &src) = delete;
	Transij &operator=(Transij &&src) = delete;

	void operator()(int index1, int index2, int &iRef, int &jRef) noexcept
	{
		iRef = trans[0] * index1 + trans[1] * index2;
		jRef = trans[2] * index1 + trans[3] * index2;
	}
};

class CalcDependVarsOneEu
{
  private:
	const real_t rgas;

  public:
	CalcDependVarsOneEu(const CalcDependVarsOneEu &src) = delete;
	CalcDependVarsOneEu(CalcDependVarsOneEu &&src) = delete;
	CalcDependVarsOneEu &operator=(const CalcDependVarsOneEu &src) = delete;
	CalcDependVarsOneEu &operator=(CalcDependVarsOneEu &&src) = delete;

	explicit CalcDependVarsOneEu(real_t R) : rgas{R} {}
	void operator()(const ConsVar_t &cv, DeptVarE_t &dv, real_t &p) const noexcept
	{
		p =
			gam1 * (cv.rhoE - 0.5 * (cv.rhou * cv.rhou + cv.rhov * cv.rhov) / cv.rho);
		dv.T = p / (rgas * cv.rho);
		dv.c = sqrt(gama * rgas * dv.T);
	}
};
class CalcDependVarsOneNS
{
  private:
	const real_t rgas, sutMu0, sutT0, sutTs;

  public:
	CalcDependVarsOneNS(real_t R, real_t sutlandMu,
						real_t sutlandT0, real_t sutlandTs)
		: rgas{R}, sutMu0{sutlandMu},
		  sutT0{sutlandT0}, sutTs{sutlandTs}
	{
	}
	CalcDependVarsOneNS(const CalcDependVarsOneNS &src) = delete;
	CalcDependVarsOneNS(CalcDependVarsOneNS &&src) = delete;
	CalcDependVarsOneNS &operator=(const CalcDependVarsOneNS &src) = delete;
	CalcDependVarsOneNS &operator=(CalcDependVarsOneNS &&src) = delete;

	void operator()(const ConsVar_t &conv, DeptVarNS_t &dpv, real_t &p)
	{
		p = gam1 * (conv.rhoE -
					0.5 * (conv.rhou * conv.rhou + conv.rhov * conv.rhov) /
						conv.rho);
		dpv.T = p / (rgas * conv.rho);
		dpv.c = sqrt(gama * rgas * dpv.T);
		dpv.mu = sutMu0 * pow(dpv.T / sutT0, 1.5) *
				 (sutT0 + sutTs) / (dpv.T + sutTs);
		dpv.ka = dpv.mu * gama / gam1 * rgas / Prl;
	}
};
/**
// inline void DependentVarsOne(const ConsVar_t &cv, DeptVarE_t &dv, real_t &p)
// {
// 	p =
// 		gam1 * (cv.rhoE - 0.5 * (cv.rhou * cv.rhou + cv.rhov * cv.rhov) / cv.rho);
// 	dv.T = p / (rgas_d * cv.rho);
// 	dv.c = sqrt(gama * rgas_d * dv.T);
// }

// inline void DependentVarsOne(const ConsVar_t &cv, DeptVarNS_t &dv)
// {
// }
**/
// 针对单块
template <typename T>
class CutExchg
{
  private:
	int i2, j2;
	// int ins[4][2]{{2, 3}, {2, 3}, {i2, i2 - 1}, {j2, j2 - 1}};
	int ins[4][2]{{2, 3}, {2, 3}, {0, 0}, {0, 0}};

  public:
	CutExchg() = default;
	CutExchg(const CutExchg &src) = delete;
	CutExchg(CutExchg &&src) = delete;
	CutExchg &operator=(const CutExchg &src) = delete;
	CutExchg &operator=(CutExchg &&src) = delete;
	~CutExchg() = default;
	CutExchg(int ii2, int jj2) : i2{ii2}, j2{jj2}
	{
		ins[2][0] = i2;
		ins[2][1] = i2 - 1;
		ins[3][0] = j2;
		ins[3][1] = j2 - 1;
	}
	void SetData(int ii2, int jj2)
	{
		i2 = ii2;
		j2 = jj2;
		ins[2][0] = i2;
		ins[2][1] = i2 - 1;
		ins[3][0] = j2;
		ins[3][1] = j2 - 1;
	}

	void operator()(const BoundDescrp_t &Bdescrp, VarArray<T, 2, 0> &var)
	{

		Transij trans(Bdescrp.isIface);
		Transij transAdj(Bdescrp.isIfaceAdj);

		const int dum1{Bdescrp.dum1}, dum2{Bdescrp.dum2},
			// 这里 Inside 应该获取邻接面的 ins1 ins2
			ins1{ins[Bdescrp.Bns][0]}, ins2{ins[Bdescrp.Bns][1]}, beg{Bdescrp.beg},
			end{Bdescrp.end}, lsteps{Bdescrp.lsteps}, ends{Bdescrp.ends};

		int pos{Bdescrp.begs};
		int idum1, jdum1, idum2, jdum2, iins1, jins1, iins2, jins2;
		for (int i = beg; i <= end; ++i)
		{
			// 使用矩阵乘的方法交换 i j的位置  下面考虑到了多块的情形
			// 三维也可这样做
			// 当前块
			trans(i, dum1, idum1, jdum1);
			trans(i, dum2, idum2, jdum2);
			// 邻接块
			transAdj(pos, ins1, iins1, jins1);
			transAdj(pos, ins2, iins2, jins2);

			var(idum1, jdum1) = var(iins1, jins1);
			var(idum2, jdum2) = var(iins2, jins2);
			pos += lsteps;
		}
		assert(pos == (ends + lsteps));
	}
};

template <typename T>
void printArray(std::ostream &os, const VarArray<T, 2, 0> &arr,
				std::string arrayName)
{
	int sz1 = arr.Dim(0);
	int sz2 = arr.Dim(1);
	os << "\n"
	   << arrayName << "(dim1 = " << sz1 << ", dim2 = " << sz2 << ")\n";

	for (int j = 0; j < sz2; ++j)
	{
		for (int i = 0; i < sz1; ++i)
		{
			os << "[" << std::setw(3) << i << "," << std::setw(3) << j << "]"
			   << arr(i, j);
		}
		os << "\n";
	}
}
// 特例化就是普通函数  在头文件只能声明， 要不然会重复定义  可以加上 inLine
// 视情况而定
template <>
void printArray<real_t>(std::ostream &os, const VarArray<real_t, 2, 0> &arr,
						std::string arrayName);

template <typename T>
void printCheck(std::ostream &os, const VarArray<T, 2, 0> &arr,
				int ibeg, int iend, int jbeg, int jend)
{
	os << ibeg << "\t" << iend << "\t" << jbeg << "\t" << jend << "\n";
	os << std::scientific << std::setprecision(17);
	for (int j = jbeg; j <= jend; ++j)
	{
		for (int i = ibeg; i <= iend; ++i)
		{
			os << i << "  " << j << "   "
			   << arr(i, j) << "\n";
		}
	}
}

/**
template <typename T>
class Farfield
{
	static real_t ua, va, pa, rhoa, mach;

  public:
	static void SetFreeStreamState(real_t uinf, real_t vinf,
								   real_t pinf, real_t rhoinf)
	{
		ua = uinf;
		va = vinf;
		pa = pinf;
		rhoa = rhoinf;
	}
	void operator()(const BoundDescrp_t &Bdescrp,
					VarArray<ConsVar_t, 2, 0> &cv,
					VarArray<T, 2, 0> &dv,
					const VarArray<Area_t, 2, 0> &si,
					const VarArray<Area_t, 2, 0> &sj)
	{
		Transij trans(Bdescrp.isIface);
		const int dum1{Bdescrp.dum1}, dum2{Bdescrp.dum2},
			ins1{Bdescrp.ins1},
			beg{Bdescrp.beg}, end{Bdescrp.end},
			indBface{(Bdescrp.Bn < 2) ? ins1 : dum1};

		const VarArray<Area_t, 2, 0> &S{isIface ? si : sj};
		real_t ds, sxn, syn, sgn{(Bdescrp.Bn < 2) ? 1.0 : -1.0};

		int i1, j1, iins, jins, idum1, jdum1, idum2, jdum2;
		const ConsVar_t cvInf{rhoa, rhoa * ua, rhoa * va,
							  pa / gam1 + 0.5 * rhoa * (ua * ua + va * va)};
		real_t pd, ud, vd, rhod, cd, qn, crho0;
		real_t pb, rhob, ub, vb;

		for (int i = beg; i <= end; ++i)
		{
			trans(i, indBface, i1, j1);
			// 注意面与单元索引的不同
			// 使面指向外部  为了区分内流和外流 inflow/outflow
			const Area_t &bs{S(i1, j1)};
			ds = sqrt(bs.sx * bs.sx + bs.sy * bs.sy);
			sxn = sgn * bs.sx / ds;
			syn = sgn * bs.sy / ds;

			trans(i, ins1, iins, jins);
			const ConsVar_t &cvIns{cv(iins, jins)};
			// 流场内部的信息
			rhod = cvIns.rho;
			ud = cvIns.rhou / rhod;
			vd = cvIns.rhov / rhod;
			// qd = ud*ud + vd*vd;
			pd = dv(iins, jins).p;
			cd = dv(iins, jins).c;
			crho0 = cd * rhod;
			qn = sxn * ud + syn * vd;

			trans(i, dum1, idum1, jdum1);
			ConsVar_t &cvDum1{cv(idum1, jdum1)};
			// 亚音速流
			if (mach < 1.0)
			{
				// inflow
				if (qn < 0.0)
				{
					pb = 0.5 * (pa + pd - crho0 * (sxn * (ua - ud) + syn * (va - vd)));
					cvDum1.rho = rhoa + (pb - pa) / (cd * cd);
					cvDum1.rhou = cvDum1.rho * (ua - sxn * (pa - pb) / crho0);
					cvDum1.rhov = cvDum1.rho * (va - syn * (pa - pb) / crho0);
					cvDum1.rhoE = pb / gam1 + 0.5 * (cvDum1.rhou * cvDum1.rhou + cvDum1.rhov * cvDum1.rhov) / cvDum1.rho;
				}
				// outflow
				else
				{
					pb = pa;
					cvDum1.rho = rhod + (pb - pd) / (cd * cd);
					cvDum1.rhou = cvDum1.rho * (ud + sxn * (pd - pb) / crho0);
					cvDum1.rhov = cvDum1.rho * (vd + syn * (pd - pb) / crho0);
					cvDum1.rhoE = pb / gam1 + 0.5 * (cvDum1.rhou * cvDum1.rhou + cvDum1.rhov * cvDum1.rhov) / cvDum1.rho;
				}
			}
			// 超音速流
			else
			{
				cvDum1 = (qn < 0.0) ? cvInf : cvIns;
			}
			// 第二层虚单元
			trans(i, dum2, idum2, jdum2);
			ConsVar_t &cvDum2{cv(idum2, jdum2)};
			cvDum2.rho = 2.0 * cvDum1.rho - cvIns.rho;
			cvDum2.rhou = 2.0 * cvDum1.rhou - cvIns.rhou;
			cvDum2.rhov = 2.0 * cvDum1.rhov - cvIns.rhov;
			cvDum2.rhoE = 2.0 * cvDum1.rhoE - cvIns.rhoE;

			DependentVarsOne(cv(idum1, jdum1));
			DependentVarsOne(cv(idum2, jdum2));
		}
	}
};
template <typename T>
real_t Farfield<T>::ua{0.0};
template <typename T>
real_t Farfield<T>::va{0.0};
template <typename T>
real_t Farfield<T>::pa{0.0};
template <typename T>
real_t Farfield<T>::rhoa{0.0};
template <typename T>
real_t Farfield<T>::mach{0.0};
**/

/**
 * @brief
 *
 * @tparam T
 * @param os
 * @param arr
 * @param arrayName
 */

/**
 * @brief 计算依赖变量
 *
 * @param cv
 * @param dv
 */

#endif